/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023.
 * Description: dump nfs debug info
 * Author: Pang LiYuan
 * Create: 2023-09-15
 */
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svc.h>
#include "rtos_debuginfo.h"

void rpc_dump_debuginfo(struct seq_file *seq, const struct rpc_version *vers)
{
	unsigned int j;
	seq_printf(seq, "proc%ucnt %u",
				vers->number, vers->nrprocs);

	for (j = 0; j < vers->nrprocs; j++) {
		if (vers->procs[j].p_debuginfo)
			seq_printf(seq, " %u", vers->procs[j].p_debuginfo->p_cnt);
		else
			seq_puts(seq, " 0");
	}
	seq_putc(seq, '\n');

	seq_printf(seq, "proc%uavg %u",
				vers->number, vers->nrprocs);
	for (j = 0; j < vers->nrprocs; j++) {
		unsigned int count = vers->counts[j];
		u64 total = 0;

		if (vers->procs[j].p_debuginfo)
			total = vers->procs[j].p_debuginfo->p_time_total;

		if (count != 0)
			do_div(total, count);
		else
			total = 0;
		seq_printf(seq, " %u", jiffies_to_msecs(total));
	}
	seq_putc(seq, '\n');

	seq_printf(seq, "proc%umax %u",
				vers->number, vers->nrprocs);
	for (j = 0; j < vers->nrprocs; j++) {
		u64 max_time = 0;

		if (vers->procs[j].p_debuginfo)
			max_time = vers->procs[j].p_debuginfo->p_max_time;
		seq_printf(seq, " %u",
			jiffies_to_msecs(max_time));
	}
	seq_putc(seq, '\n');
}

void svc_dump_debuginfo(struct seq_file *seq, const struct svc_version *vers, int idx)
{
	const struct svc_procedure *proc, *svc_proc;
	int j = 0;

	svc_proc = vers->vs_proc;
	if (!svc_proc)
		return;

	proc = svc_proc;
	seq_printf(seq, "proc%dcnt %u", idx, vers->vs_nproc);
	for (j = 0; j < vers->vs_nproc; j++, proc++) {
		if (proc->pc_debuginfo)
			seq_printf(seq, " %u", proc->pc_debuginfo->pc_cnt);
		else
			seq_puts(seq, " 0");
	}
	seq_putc(seq, '\n');

	proc = svc_proc;
	seq_printf(seq, "proc%davg %u", idx, vers->vs_nproc);
	for (j = 0; j < vers->vs_nproc; j++, proc++) {
		unsigned int count = vers->vs_count[j];
		u64 total = 0;

		if (proc->pc_debuginfo)
			total = proc->pc_debuginfo->pc_time_total;

		if (count != 0)
			do_div(total, count);
		else
			total = 0;
		seq_printf(seq, " %u", jiffies_to_msecs(total));
	}
	seq_putc(seq, '\n');

	proc = svc_proc;
	seq_printf(seq, "proc%dmax %u", idx, vers->vs_nproc);
	for (j = 0; j < vers->vs_nproc; j++, proc++) {
		u64 max_time = 0;

		if (proc->pc_debuginfo)
			max_time = proc->pc_debuginfo->pc_max_time;
		seq_printf(seq, " %u", jiffies_to_msecs(max_time));
	}
	seq_putc(seq, '\n');
}

static void clear_rpc_stat(struct rpc_stat *statp)
{
	const struct rpc_program *prog = statp->program;
	unsigned int i, j;

	for (i = 0; i < prog->nrvers; i++) {
		const struct rpc_version *vers = prog->version[i];
		if (!vers)
			continue;

		if (!vers->procs)
			continue;

		for (j = 0; j < vers->nrprocs; j++)
			if (vers->procs[j].p_debuginfo)
				vers->procs[j].p_debuginfo->p_cnt = 0;
	}
}

ssize_t rpc_proc_write_clnt_throttle(struct file *file,
		const char __user *buf, size_t count, loff_t *offs)
{
	unsigned long val;
	int ret;
	struct inode *inode = file->f_inode;
	struct rpc_stat *statp = PDE_DATA(inode);

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;

	ret = kstrtoul_from_user(buf, count, 0, &val);
	if (ret < 0)
		return ret;

	nfs_set_rpc_clnt_throttle(val);
	if (statp)
		clear_rpc_stat(statp);

	return count;
}